﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NeuralNetworks
{
    class PlotConverter
    {
        private int FirstLayerNeurons { get; set; }
        private int RandomDotsAmount { get; set; }

        /// <summary>
        /// Main converter constructor.
        /// </summary>
        /// <param name="resolution">Amount of neurons of the first layer on one axis of the square.</param>
        public PlotConverter(int resolution, int randomDotsAmount)
        {
            if (resolution >= 10)
                FirstLayerNeurons = (int)Math.Pow(resolution, 2);
            else throw new ArgumentException("To small amount of neurons.");
            this.RandomDotsAmount = randomDotsAmount;
        }

        /// <summary>
        /// Returns the 2-dim array of values for the 1 layer of neural network
        /// </summary>
        /// <param name="C">Constant near the exponent (amplitude)</param>
        public double[] ConvertExponent(double C, double A, double xStart, double xEnd, double yStart, double yEnd)
        {
            if (xStart < xEnd && yStart < yEnd)
            {
                int length = (int)Math.Sqrt(FirstLayerNeurons);
                double[,] result = new double[length, length]; // creating the result array (will be put on the neural input)

                // counting the cell height and cell width
                double cellHeight = (yEnd - yStart) / length;
                double cellWidth = (xEnd - xStart) / length;

                double squareS = cellHeight * cellWidth; // count the square space for monte-carlo method counting

                // main algorithm: count space under line for every cell (from 0 to 1)
                double underPlotDotsCounter = 0;
                double currentXStart = xStart, currentYStart = yEnd; // we substract cellHeight from yStart 'cause yStart is the top left point of the big square
                Random r = new Random();
                double randomX, randomY;
                // we will go down by columns and then right by rows (зліва направо по стовпчикам)
                for (int i = 0; i < length; i++) // for OX
                {
                    // every time we will have coordinates of LEFT BOTTOM POINT of the square
                    for (int j = 0; j < length; j++) // for OY
                    {
                        currentYStart -= cellHeight;
                        for (int l = 0; l < RandomDotsAmount; l++) // throwing many random dots into the square
                        {
                            // random.NextDouble() * (maximum - minimum) + minimum;
                            randomX = r.NextDouble() * cellWidth + currentXStart; // x
                            randomY = r.NextDouble() * cellHeight + currentYStart; // y

                            // verifying if the dot is under the plot line
                            if (randomY <= CountFunction(randomX, C, A)) // if the random Y value is under (smaller than) the function value
                                underPlotDotsCounter++;
                        }

                        // saving the result
                        result[i, j] = squareS * (underPlotDotsCounter / RandomDotsAmount); // squareSpace * (dotsUnderGraphicLine / generalDotsAmount) from 0 to 1
                        underPlotDotsCounter = 0;
                    }
                    currentYStart = yEnd;
                    currentXStart += cellWidth;
                }

                return ConvertTwoDimToOneDim(result);
            }
            else throw new ArgumentException("Error with input parameters.");
        }

        /// <summary>
        /// Returns the exponential function value   
        /// </summary>
        /// <param name="x">X value</param>
        /// <param name="C">Near exponent constant value</param>
        /// <param name="A">In exponent power value</param>
        private double CountFunction(double x, double C, double A)
        { return C * Math.Exp(-1 * x * A); }

        /// <summary>
        /// Converts the 2-dim array to 1-dim array
        /// </summary>
        private double[] ConvertTwoDimToOneDim(double[,] array)
        {
            double[] result = new double[(int)Math.Pow(array.GetLength(0), 2)];
            int i = 0;
            foreach (double item in array)
            {
                result[i] = item;
                i++;
            }

            return result;
        }
    }
}
